﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AMS.Chess.CommonLayer
{
    public class StandardChessBoard : AChessBoard
    {
        public static readonly int MinRank = 1;
        public static readonly int MaxRank = 8;
        public static readonly char MinFile = 'a';
        public static readonly char MaxFile = 'h';

        private Dictionary<RankFilePair, AChessPiece> _positionsToPieces;

        public StandardChessBoard(IChessPieceFactory pieceFactory)
        {
            _pieceFactory = pieceFactory;
            _positionsToPieces = new Dictionary<RankFilePair, AChessPiece>();

            SetUpBoard();
        }

        // chess board is responsible for keeping track of pieces

        #region Setup Methods
        private void SetUpBoard()
        {
            // instantiate and position all pieces
            // TODO: move start to readonly definition
            SetPosition(_pieceFactory.CreateKing(this, ChessColor.Black), 8, 'e');
            SetPosition(_pieceFactory.CreateQueen(this, ChessColor.Black), 8, 'd');
            SetPosition(_pieceFactory.CreateRook(this, ChessColor.Black), 8, 'h');
            SetPosition(_pieceFactory.CreateRook(this, ChessColor.Black), 8, 'a');
            SetPosition(_pieceFactory.CreateBishop(this, ChessColor.Black), 8, 'c');
            SetPosition(_pieceFactory.CreateBishop(this, ChessColor.Black), 8, 'f');
            SetPosition(_pieceFactory.CreateKnight(this, ChessColor.Black), 8, 'b');
            SetPosition(_pieceFactory.CreateKnight(this, ChessColor.Black), 8, 'g');
            SetPosition(_pieceFactory.CreatePawn(this, ChessColor.Black), 7, 'a');
            SetPosition(_pieceFactory.CreatePawn(this, ChessColor.Black), 7, 'b');
            SetPosition(_pieceFactory.CreatePawn(this, ChessColor.Black), 7, 'c');
            SetPosition(_pieceFactory.CreatePawn(this, ChessColor.Black), 7, 'd');
            SetPosition(_pieceFactory.CreatePawn(this, ChessColor.Black), 7, 'e');
            SetPosition(_pieceFactory.CreatePawn(this, ChessColor.Black), 7, 'f');
            SetPosition(_pieceFactory.CreatePawn(this, ChessColor.Black), 7, 'g');
            SetPosition(_pieceFactory.CreatePawn(this, ChessColor.Black), 7, 'h');

            SetPosition(_pieceFactory.CreateKing(this, ChessColor.White), 1, 'e');
            SetPosition(_pieceFactory.CreateQueen(this, ChessColor.White), 1, 'd');
            SetPosition(_pieceFactory.CreateRook(this, ChessColor.White), 1, 'h');
            SetPosition(_pieceFactory.CreateRook(this, ChessColor.White), 1, 'a');
            SetPosition(_pieceFactory.CreateBishop(this, ChessColor.White), 1, 'c');
            SetPosition(_pieceFactory.CreateBishop(this, ChessColor.White), 1, 'f');
            SetPosition(_pieceFactory.CreateKnight(this, ChessColor.White), 1, 'b');
            SetPosition(_pieceFactory.CreateKnight(this, ChessColor.White), 1, 'g');
            SetPosition(_pieceFactory.CreatePawn(this, ChessColor.White), 2, 'a');
            SetPosition(_pieceFactory.CreatePawn(this, ChessColor.White), 2, 'b');
            SetPosition(_pieceFactory.CreatePawn(this, ChessColor.White), 2, 'c');
            SetPosition(_pieceFactory.CreatePawn(this, ChessColor.White), 2, 'd');
            SetPosition(_pieceFactory.CreatePawn(this, ChessColor.White), 2, 'e');
            SetPosition(_pieceFactory.CreatePawn(this, ChessColor.White), 2, 'f');
            SetPosition(_pieceFactory.CreatePawn(this, ChessColor.White), 2, 'g');
            SetPosition(_pieceFactory.CreatePawn(this, ChessColor.White), 2, 'h');
        }

        private void SetPosition(AChessPiece piece, int rank, char file)
        {
            _positionsToPieces.Add(new RankFilePair(rank, file), piece);
            piece.SetPosition(new StandardBoardPosition(rank, file));
        }
        #endregion

        //public void SetPiece(int rank, char file, AChessPiece piece)
        //{
        //    file = char.ToLower(file);
            
        //    if (!IsValidRank(rank))
        //        throw new ArgumentOutOfRangeException("Invalid rank.  Must be between 1 and 8, inclusive.");
        //    if (!IsValidFile(file))
        //        throw new ArgumentOutOfRangeException("invalid file.  Must be between A and H, inclusive.");
            
        //    // validate that this is a legit move
        //    List<ABoardPosition> legitMoves = piece.GetPossibleMoves();
            

        //}

        // undo move variables
        private AChessPiece lastCapturedPiece;
        private AChessPiece lastMovedPiece;
        private ABoardPosition lastMoveStart;
        private ABoardPosition lastMoveEnd;

        public override void ChangePiecePosition(AChessPiece piece, ABoardPosition startPosition, ABoardPosition endPosition)
        {
            // translate start and end position
            StandardBoardPosition s = startPosition as StandardBoardPosition;
            StandardBoardPosition e = endPosition as StandardBoardPosition;
            RankFilePair startPair = new RankFilePair(s.Rank, s.File);
            RankFilePair endPair = new RankFilePair(e.Rank, e.File);

            // validate this piece is on start position
            if (!_positionsToPieces.ContainsKey(startPair) || !_positionsToPieces[startPair].Equals(piece))
            {
                throw new InvalidOperationException("Specified piece is not on the specified startPosition");
            }

            // validate endPosition is a valid move position
            piece.IsValidMoveTarget(endPosition);

            // if space is emtpy, no problem
            if (!_positionsToPieces.ContainsKey(endPair))
            {
                lastCapturedPiece = null;
            }
            else
            {
                AChessPiece occupier = _positionsToPieces[endPair];
                
                // if space is occupied by friendly piece, throw exception
                if (occupier.Color == piece.Color)
                {
                    throw new InvalidOperationException("Can't attempt to capture friendly piece");
                }
                else
                {
                    // if space is occupied by opponent piece, capture it
                    occupier.Captured();
                    lastCapturedPiece = occupier;
                }
            }

            lastMovedPiece = piece;
            lastMoveStart = startPosition;
            lastMoveEnd = endPosition;

            // execute move
            _positionsToPieces.Remove(startPair);
            _positionsToPieces.Add(endPair, piece);
            piece.SetPosition(endPosition);
        }

        public bool IsValidRank(int rank)
        {
            return (rank >= MinRank && rank <= MaxRank);
        }

        public bool IsValidFile(char file)
        {
            file = char.ToLower(file);
            return (file >= MinFile && file <= MaxFile);
        }

        public override bool IsValidPosition(ABoardPosition position)
        {
            StandardBoardPosition  p = position as StandardBoardPosition;

            if (p == null)
                return false;

            return IsValidRank(p.Rank) && IsValidFile(p.File);
        }

        public override AChessPiece GetPiece(ABoardPosition position)
        {
            StandardBoardPosition sp = position as StandardBoardPosition;
            if (sp == null)
                throw new InvalidOperationException("invalid position type");

            RankFilePair pair = new RankFilePair(sp.Rank, sp.File);

            if (_positionsToPieces.ContainsKey(pair))
            {
                return _positionsToPieces[pair];
            }

            return null;
        }

        public override bool ContainsPiece(ABoardPosition position)
        {
            StandardBoardPosition sp = position as StandardBoardPosition;

            if (sp == null)
                return false;

            RankFilePair pair = new RankFilePair(sp.Rank, sp.File);

            if (_positionsToPieces.ContainsKey(pair))
            {
                return _positionsToPieces[pair] != null;
            }

            return false;
        }

        // inner class
        // wanted pass-by-value, so used this instead of StandardBoardPosition
        internal struct RankFilePair
        {
            private int _rank;
            public int Rank
            {
                get { return _rank; }
                set { _rank = value;  }
            }

            private char _file;
            public char File
            {
                get { return _file; }
                set { _file = char.ToLower(value); }
            }

            public RankFilePair(int rank, char file)
            {
                _rank = rank;
                _file = file;
            }

            public static bool operator == (RankFilePair a, RankFilePair b)
            {
                return a.File == b.File && a.Rank == b.Rank;
            }

            public static bool operator != (RankFilePair a, RankFilePair b)
            {
                return !(a == b);
            }
        }
    }
}
